/**
 * DVN: Distributed Virtual Network Simulation Platform
 * Copyright (c) 2006-2009 Tyson Malchow <tyson@trmotics.com>
 *
 * This file is part of the DVN distributed virtual network simulation
 * program.
 *
 * DVN is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * DVN is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with DVN.  If not, see <http://www.gnu.org/licenses/>.
 */

#include "global.h"
#include "context.h"
#include "sim.h"
#include "timer.h"
#include "vci.h"

struct context_sys_t global_sim_context;
static void context_swap(context_provider_tp provider);
static void context_unswap(void);

static void context_swap(context_provider_tp provider) {
	if(provider != global_sim_context.current_context) {
		global_sim_context.current_context = provider;

		if(global_sim_context.current_context)
			module_load_globals(provider->modinst);
	}
}

static void context_unswap(void) {
	if(global_sim_context.current_context) {
		module_save_globals(global_sim_context.current_context->modinst);
		global_sim_context.current_context = NULL;
	}
}

void context_set_worker(sim_worker_tp wo) {
	global_sim_context.sim_worker = wo;
}

void context_execute_init(module_tp module) {
	global_sim_context.static_context = module;
	module_call_init(module);
	global_sim_context.static_context = NULL;
}

void context_execute_uninit(module_tp module) {

}

void context_execute_instantiate(context_provider_tp provider, in_addr_t bootstrap_addr, int argc, char* argv[]) {
	if(!provider)
		return;

	/* swap out env for this provider */
	context_swap(provider);
	global_sim_context.exit_usable = 1;

	if(setjmp(global_sim_context.exit_env) == 1)  /* module has been destroyed if we get here. (sim_context.current_context will be NULL) */
		return;

	else
		module_call_instantiate(provider->modinst, bootstrap_addr, argc, argv);

	/* swap back to dvn holding */
	context_unswap();

	return;
}

void context_execute_destroy(context_provider_tp provider) {
	/* swap out env for this provider */
	context_swap(provider);
	global_sim_context.exit_usable = 0;

	/* send out destroy event */
	module_call_destroy(provider->modinst);

	/* we don't swap back here ... module is "destroyed" */

	return;
}

void context_execute_dtimer_cb(context_provider_tp provider, dtimer_ontimer_cb_fp cb, int timer_id, void * cb_arg) {
	if(!provider)
		return;

	context_swap(provider);
	global_sim_context.exit_usable = 1;

	if(setjmp(global_sim_context.exit_env) == 1) /* module has been destroyed if we get here. (sim_context.current_context will be NULL) */
			return;

	else
		(*cb)(timer_id, cb_arg);

	/* swap back to dvn holding */
	context_unswap();

	return;
}

void context_execute_vci_onrecv(context_provider_tp provider, vci_onrecv_cb_tp cb, int sock, in_addr_t source_address,
		int source_port, unsigned int data_size, char * data) {

	if(!provider)
		return;

	/* swap out env for this provider */
	context_swap(provider);
	global_sim_context.exit_usable = 1;

	if(setjmp(global_sim_context.exit_env) == 1) /* module has been destroyed if we get here. (sim_context.current_context will be NULL) */
		return;

	else
		(*cb)(sock,source_address,source_port,data_size,data);

	/* swap back to dvn holding */
	context_unswap();

	return;
}
